package cn.zyq.ykb.service.config.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.zyq.ykb.beans.config.ConfigProp;
import cn.zyq.ykb.beans.config.ConfigPropOrder;
import cn.zyq.ykb.beans.config.ConfigRecord;
import cn.zyq.ykb.beans.pay.PayOrder;
import cn.zyq.ykb.beans.task.Task;
import cn.zyq.ykb.beans.task.TaskOrder;
import cn.zyq.ykb.beans.tk.TUser;
import cn.zyq.ykb.beans.user.User;
import cn.zyq.ykb.mapper.config.ConfigPropOrderMapper;
import cn.zyq.ykb.mapper.config.ConfigRecordMapper;
import cn.zyq.ykb.mapper.task.TaskMapper;
import cn.zyq.ykb.mapper.tk.TUserMapper;
import cn.zyq.ykb.mapper.user.UserMapper;
import cn.zyq.ykb.service.config.ConfigPropOrderService;
import cn.zyq.ykb.service.pay.PayOrderService;
import cn.zyq.ykb.system.common.BaseService;
import cn.zyq.ykb.system.enums.core.*;
import cn.zyq.ykb.system.exception.BusinessException;
import cn.zyq.ykb.system.pay.core.enums.PayChannelEnum;
import cn.zyq.ykb.system.pay.core.enums.PayOrderStatusEnum;
import cn.zyq.ykb.system.task.delayQueue.DelayQueueManager;
import cn.zyq.ykb.system.task.delayQueue.DelayTask;
import cn.zyq.ykb.system.task.delayQueue.TaskBase;
import cn.zyq.ykb.system.utils.DateUtils;
import cn.zyq.ykb.system.utils.IPUtil;
import com.github.pagehelper.PageHelper;
import cn.zyq.ykb.system.ry.util.SecurityUtils;
import cn.zyq.ykb.mapper.config.ConfigPropMapper;
import cn.zyq.ykb.service.config.ConfigPropService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import cn.zyq.ykb.system.common.PageParam;
import com.github.pagehelper.PageInfo;
import org.springframework.transaction.annotation.Transactional;


import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;

/**
 * 置顶道具配置Service业务层处理
 *
 * @author SystemAuth
 * @date 2023-05-24
 */
@Service
@Slf4j
public class ConfigPropServiceImpl extends BaseService implements ConfigPropService {

    @Autowired
    private ConfigPropMapper configPropMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private ConfigPropOrderMapper configPropOrderMapper;
    @Autowired
    private PayOrderService payOrderService;
    @Autowired
    private TaskMapper taskMapper;
    @Autowired
    private ConfigRecordMapper configRecordMapper;
    @Autowired
    private DelayQueueManager delayQueueManager;
    @Autowired
    private ConfigPropOrderService configPropOrderService;
    @Autowired
    private TUserMapper tUserMapper;

    /**
     * 查询置顶道具配置
     *
     * @param configPropID 置顶道具配置主键
     * @return 置顶道具配置
     */
    @Override
    public ConfigProp selectConfigPropByConfigPropID(Integer configPropID) {
        ConfigProp configProp = configPropMapper.selectByPrimaryKey(configPropID);
        if (configProp.getDeleted() == 1) {
            return null;
        }
        return configProp;
    }

    /**
     * 查询置顶道具配置
     *
     * @param serviceID 业务ID
     * @return 置顶道具配置
     */
    @Override
    public ConfigProp selectConfigPropByServiceID(String serviceID) {
        ConfigProp param = new ConfigProp();
        param.setDeleted(0);
        param.setServiceID(serviceID);
        return this.configPropMapper.selectOne(param);
    }

    /**
     * 查询置顶道具配置列表
     *
     * @param pageParam
     * @return 置顶道具配置
     */
    @Override
    public PageInfo<ConfigProp> selectConfigPropList(PageParam pageParam) {
        PageHelper.startPage(pageParam.getPage(), pageParam.getPageSize());
        pageParam.getKv().put("deleted", 0);
        List<ConfigProp> list = this.configPropMapper.list(pageParam.getKv());
        return new PageInfo<>(list);
    }

    /**
     * 查询置顶道具配置列表
     *
     * @param param
     * @return 置顶道具配置
     */
    @Override
    public List<ConfigProp> selectConfigPropList(ConfigProp param) {
        param.setDeleted(0);
        return this.configPropMapper.select(param);
    }
    /**
     * 新增置顶道具配置
     *
     * @param param 置顶道具配置
     * @return 结果
     */
    @Override
    public int insertConfigProp(ConfigProp param) {
        param.setSinglePrice(param.getPrice().divide(new BigDecimal(param.getNumber())));
        this.setCreateBaseData(param, SecurityUtils.getLoginUser().getSysAdmin());
        return this.configPropMapper.insertSelective(param);
    }

    /**
     * 修改置顶道具配置
     *
     * @param param 置顶道具配置
     * @return 结果
     */
    @Override
    public int updateConfigProp(ConfigProp param) {
        this.setUpdateBaseData(param, SecurityUtils.getLoginUser().getSysAdmin());
        return this.configPropMapper.updateByPrimaryKeySelective(param);
    }

    /**
     * 批量删除置顶道具配置
     *
     * @param configPropIDs 需要删除的置顶道具配置主键
     * @return 结果
     */
    @Override
    public int deleteConfigPropByConfigPropIDs(Integer[] configPropIDs) {
        int i = 0;
        for (Integer configPropID : configPropIDs) {
            i += this.deleteConfigPropByConfigPropID(configPropID);
        }
        return i;
    }

    /**
     * 删除置顶道具配置信息
     *
     * @param configPropID 置顶道具配置主键
     * @return 结果
     */
    @Override
    public int deleteConfigPropByConfigPropID(Integer configPropID) {
        ConfigProp configProp = this.selectConfigPropByConfigPropID(configPropID);
        if (configProp == null) {
            return 0;
        } else {
            ConfigProp storeInfo = new ConfigProp();
            storeInfo.setConfigPropID(configPropID);
            storeInfo.setDeleted(1);
            storeInfo.setUpdateBy(SecurityUtils.getSysAdminID());
            storeInfo.setUpdateTime(LocalDateTime.now());
            this.configPropMapper.updateByPrimaryKeySelective(storeInfo);
            return 1;
        }
    }

    @Override
    public ConfigPropOrder propOrder(ConfigProp pageParam, HttpServletRequest request, User loginUser) {
        ConfigProp configProp = this.configPropMapper.selectByPrimaryKey(pageParam.getConfigPropID());
        if (BeanUtil.isEmpty(configProp)){
            throw new BusinessException(ResponseCodeEnum.ERROR,"当前道具数据不存在");
        }
        String bodyInfo = "";
        if (configProp.getType().equals(OrderTypeEnum.GM.getValue())){
            bodyInfo = "刷新";
        }else {
            bodyInfo = "置顶";
        }
        User user = this.userMapper.selectByPrimaryKey(loginUser.getUserID());
        this.checkLogicExist(user, "没有获取到下单用户信息");

        String orderCode = OrderTypeEnum.GM.getMarker() + loginUser.getPhone() + System.currentTimeMillis();
        ConfigPropOrder configPropOrder = new ConfigPropOrder();
        configPropOrder.setOrderCode(orderCode);
        configPropOrder.setConfigPropID(pageParam.getConfigPropID());
        configPropOrder.setUserID(loginUser.getUserID());
        configPropOrder.setPayType(PayTypeEnum.ZFB.getValue());
        configPropOrder.setPrice(pageParam.getPrice());
        configPropOrder.setOrderState(1);
        configPropOrder.setPayTime(LocalDateTime.now());
        configPropOrder.setPayExpireTime(LocalDateTime.now().plusMinutes(5L));
        this.setCreateBaseData(configPropOrder,loginUser);
        int i = this.configPropOrderMapper.insertSelective(configPropOrder);
        if (i == 0){
            throw new BusinessException(ResponseCodeEnum.ERROR,"新增道具订单失败，下单失败");
        }
        Integer channelId=0;
        if (pageParam.getPhoneSystems().equals(1)){
            channelId = PayChannelEnum.ALIPAY_APP_ANDROID.getChannelId();
        }else if (pageParam.getPhoneSystems().equals(2)){
            channelId = PayChannelEnum.ALIPAY_WAP.getChannelId();
        }else {
            throw new BusinessException(ResponseCodeEnum.ERROR,"请传递正确的支付渠道码");
        }
        //去下单
        PayOrder payOrder = new PayOrder(
                channelId,
                orderCode,
                "购买"+bodyInfo+"道具下单",
                bodyInfo+"道具",
                IPUtil.getIPAddress(request),
                loginUser.getUserID(),
                PayOrderTypeEnum.SX.getValue(),
                configPropOrder.getConfigPropOrderID(),
                pageParam.getPrice(),
                PayOrderBusinessTypeEnum.GMDJ.getValue(),
                new BigDecimal(0),
                LocalDateTime.now().plusMinutes(5L)
                );
        PayOrder pay = this.payOrderService.pay(payOrder, loginUser);
        String payReqRespInfo = pay.getPayReqResp();
        configPropOrder.setPayReqRespInfo(payReqRespInfo);
        if (pay.getExpireTime() != null) {
            long l = DateUtils.durationToMillis(LocalDateTime.now(), pay.getExpireTime());
            //加入延时队列
            TaskBase base = new TaskBase(configPropOrder.getServiceID(), 2, configPropOrder.getServiceID());
            //获取相差时间
            DelayTask task1 = new DelayTask(base, l);
            this.delayQueueManager.put(task1);
            log.info("订单：{}加入延时队列,过期时间{}", configPropOrder.getServiceID(), pay.getExpireTime());
        }
        return configPropOrder;
    }

    @Override
    public void payNotifyOrder(PayOrder payOrder) {
        log.info("进去购买道具下单回调 {}",payOrder);
        //获取原订单
        ConfigPropOrder configPropOrder = new ConfigPropOrder();
        configPropOrder.setOrderCode(payOrder.getOrderCode());
        configPropOrder.setConfigPropOrderID(payOrder.getRelatedOrderID());
        ConfigPropOrder configPropOrder1 = this.configPropOrderMapper.selectOne(configPropOrder);
        this.checkLogicExist(configPropOrder1, "没有获取到关联订单");

        ConfigPropOrder updateInstance = this.getUpdateInstance(ConfigPropOrder.class);
        updateInstance.setConfigPropOrderID(configPropOrder1.getConfigPropOrderID());
        //判断支付是否成功
        if (PayOrderStatusEnum.NOTIFY_SUCCESS.getValue().equals(payOrder.getStatus())) {
            updateInstance.setOrderState(TaskOrderStateEnum.ZFCG.getValue());
            updateInstance.setFinishTime(LocalDateTime.now());
            updateInstance.setActualPayAmount(payOrder.getNotifyPayerAmount());
            //获取道具信息
            ConfigProp configProp = this.configPropMapper.selectByPrimaryKey(configPropOrder1.getConfigPropID());
            //获取用户信息
            User user = this.userMapper.selectByPrimaryKey(configPropOrder1.getUserID());
            //如果是刷新道具就增加用户的刷新道具数量
            if (configProp.getType().equals(OrderTypeEnum.GM.getValue())){
                user.setBreakNum(user.getBreakNum() + configProp.getNumber());
            }else {
                user.setTopNum(user.getTopNum() + configProp.getNumber());
            }
            this.userMapper.updateByPrimaryKeySelective(user);
            log.info("给用户增加道具数量成功 {}",configProp.getNumber());
        }else {
            updateInstance.setOrderState(TaskOrderStateEnum.ZFSB.getValue());
        }
        this.configPropOrderMapper.updateByPrimaryKeySelective(updateInstance);
        this.delayQueueManager.remove(configPropOrder.getServiceID());
    }

    @Override
    @Transactional
    public int sxProp(Integer taskID, User loginUser) {
        Task task = this.taskMapper.selectByPrimaryKey(taskID);
        this.checkLogicExist(task, "没有获取当前任务");
        User user = this.userMapper.selectByPrimaryKey(loginUser.getUserID());
        if (!user.getPosition().equals(UserPositionEnum.FWS.getValue()) && !user.getPosition().equals(UserPositionEnum.GR.getValue())){
            throw new BusinessException(ResponseCodeEnum.ERROR,"只有服务商级别才能刷新任务");
        }
        if (user.getBreakNum() == 0){
            throw new BusinessException(ResponseCodeEnum.ERROR,"当前剩余刷新道具为零，请购买后刷新");
        }
        //扣减用户剩余道具数量
        user.setBreakNum(user.getBreakNum() - 1);
        this.setUpdateBaseData(user,loginUser);
        int i = this.userMapper.updateByPrimaryKeySelective(user);
        if (i == 0){
            throw new BusinessException(ResponseCodeEnum.ERROR,"扣减用户剩余道具数据失败");
        }
        //修改任务
        task.setSxProp(System.currentTimeMillis());
        task.setSxExpireTime(LocalDateTime.now().minus(-30, ChronoUnit.DAYS));
        this.setUpdateBaseData(task,loginUser);
        int i1 = this.taskMapper.updateByPrimaryKeySelective(task);
        if (i1 == 0){
            throw new BusinessException(ResponseCodeEnum.ERROR,"修改任务刷新失败");
        }
        //增加操作记录表数据
        ConfigRecord configRecord = new ConfigRecord(
                loginUser.getUserID(),
                ConfigRecordTypeEnum.SX.getValue(),
                taskID,
                "对任务ID："+taskID+"进行了刷新"
        );
        this.setCreateBaseData(configRecord,loginUser);
        int i2 = this.configRecordMapper.insertSelective(configRecord);
        if (i2 == 0){
            throw new BusinessException(ResponseCodeEnum.ERROR,"新增操作记录表数据失败");
        }
        return 1;
    }

    @Override
    public int zdProp(Integer taskID, User loginUser) {
        Task task = this.taskMapper.selectByPrimaryKey(taskID);
        this.checkLogicExist(task, "没有获取当前任务");
        User user = this.userMapper.selectByPrimaryKey(loginUser.getUserID());
        if (user.getTopNum() == 0){
            throw new BusinessException(ResponseCodeEnum.ERROR,"当前剩余置顶道具为零，请购买后置顶");
        }
        //扣减用户剩余道具数量
        user.setBreakNum(user.getTopNum() - 1);
        this.setUpdateBaseData(user,loginUser);
        int i = this.userMapper.updateByPrimaryKeySelective(user);
        if (i == 0){
            throw new BusinessException(ResponseCodeEnum.ERROR,"扣减用户剩余道具数据失败");
        }
        //修改任务
        task.setZdProp(System.currentTimeMillis());
        task.setZdExpireTime(LocalDateTime.now().minus(-30, ChronoUnit.DAYS));
        this.setUpdateBaseData(task,loginUser);
        int i1 = this.taskMapper.updateByPrimaryKeySelective(task);
        if (i1 == 0){
            throw new BusinessException(ResponseCodeEnum.ERROR,"修改任务刷新失败");
        }
        //增加操作记录表数据
        ConfigRecord configRecord = new ConfigRecord(
                loginUser.getUserID(),
                ConfigRecordTypeEnum.ZD.getValue(),
                taskID,
                "对任务ID："+taskID+"进行了置顶"
        );
        this.setCreateBaseData(configRecord,loginUser);
        int i2 = this.configRecordMapper.insertSelective(configRecord);
        if (i2 == 0){
            throw new BusinessException(ResponseCodeEnum.ERROR,"新增操作记录表数据失败");
        }
        return 1;
    }

    @Override
    public void checkExpire(String serviceID) {
        ConfigPropOrder configPropOrder = this.configPropOrderService.selectConfigPropOrderByServiceID(serviceID);
        if (BeanUtil.isEmpty(configPropOrder)){
            throw new BusinessException(ResponseCodeEnum.ERROR,"道具订单不存在");
        }
        if (TaskOrderStateEnum.DZF.getValue().equals(configPropOrder.getOrderState())) {
            if (LocalDateTime.now().isAfter(configPropOrder.getPayExpireTime())
                    || LocalDateTime.now().equals(configPropOrder.getPayExpireTime())) {
                //已过期
                log.info("订单{}已被处理为延时未支付", configPropOrder.getServiceID());
                configPropOrder.setOrderState(TaskOrderStateEnum.ZFCS.getValue());
                this.configPropOrderMapper.updateByPrimaryKeySelective(configPropOrder);
            }
        }else {
            //重新加回队列
            TaskBase base = new TaskBase(configPropOrder.getServiceID(), 2, configPropOrder.getServiceID());
            //获取相差时间
            long l = DateUtils.durationToMillis(LocalDateTime.now(), configPropOrder.getPayExpireTime());
            if (l > 0) {
                DelayTask task = new DelayTask(base, l);
                this.delayQueueManager.put(task);
            }
        }


    }

    @Override
    public void reloadStateQueue() {
        log.info("初始化未支付的道具订单进入延时队列");
        PageParam pageParam = new PageParam();
        pageParam.setPage(1);
        pageParam.setPageSize(1000);
        boolean flag = true;
        while (flag) {
            //获取未支付的数据
            PageHelper.startPage(pageParam.getPage(), pageParam.getPageSize());
            List<ConfigPropOrder> list = this.configPropOrderMapper.queryNoPayOrder();
            for (ConfigPropOrder order : list) {
                if (LocalDateTime.now().isBefore(order.getPayExpireTime())) {
                    //加入延时队列
                    TaskBase base = new TaskBase(order.getServiceID(), 2, order.getServiceID());
                    long l = DateUtils.durationToMillis(LocalDateTime.now(), order.getPayExpireTime());
                    //获取相差时间
                    DelayTask task = new DelayTask(base, l);
                    this.delayQueueManager.put(task);
                    log.info("订单：{}加入延时队列，延时毫秒{}，过期时间：{}", order.getServiceID(), l, order.getPayExpireTime());
                } else {
                    log.info("订单：{}已过期", order.getServiceID());
                    //已过期
                    ConfigPropOrder updateOrder = new ConfigPropOrder();
                    updateOrder.setOrderState(TaskOrderStateEnum.ZFCS.getValue());
                    updateOrder.setConfigPropOrderID(order.getConfigPropOrderID());
                    this.configPropOrderMapper.updateByPrimaryKeySelective(updateOrder);
                }
            }
            //当获取的列表小于分页大小，则没有更多数据，停止轮询
            if (list.size() < pageParam.getPageSize()) {
                flag = false;
            } else {
                pageParam.setPage(pageParam.getPage() + 1);
            }
        }
    }

    @Override
    public ConfigPropOrder pay(ConfigProp pageParam, HttpServletRequest request) {
        ConfigProp configProp = this.configPropMapper.selectByPrimaryKey(pageParam.getConfigPropID());
        if (BeanUtil.isEmpty(configProp)){
            throw new BusinessException(ResponseCodeEnum.ERROR,"当前道具数据不存在");
        }
        String bodyInfo = "";
        if (configProp.getType().equals(OrderTypeEnum.GM.getValue())){
            bodyInfo = "刷新";
        }else {
            bodyInfo = "置顶";
        }
        TUser user = this.tUserMapper.selectByPrimaryKey(pageParam.getTUserID());
        this.checkLogicExist(user, "没有获取到下单用户信息");

        String orderCode = OrderTypeEnum.GM.getMarker() + user.getPhone() + System.currentTimeMillis();
        ConfigPropOrder configPropOrder = new ConfigPropOrder();
        configPropOrder.setOrderCode(orderCode);
        configPropOrder.setConfigPropID(pageParam.getConfigPropID());
        configPropOrder.setUserID(pageParam.getTUserID());
        configPropOrder.setPayType(PayTypeEnum.ZFB.getValue());
        configPropOrder.setPrice(pageParam.getPrice());
        configPropOrder.setOrderState(1);
        configPropOrder.setPayTime(LocalDateTime.now());
        configPropOrder.setPayExpireTime(LocalDateTime.now().plusMinutes(5L));
        this.setCreateBaseData(configPropOrder,new User());
        int i = this.configPropOrderMapper.insertSelective(configPropOrder);
        if (i == 0){
            throw new BusinessException(ResponseCodeEnum.ERROR,"新增道具订单失败，下单失败");
        }
        Integer channelId=0;
        if (pageParam.getPhoneSystems().equals(1)){
            channelId = PayChannelEnum.ALIPAY_APP_ANDROID.getChannelId();
        }else if (pageParam.getPhoneSystems().equals(2)){
            channelId = PayChannelEnum.ALIPAY_WAP.getChannelId();
        }else {
            throw new BusinessException(ResponseCodeEnum.ERROR,"请传递正确的支付渠道码");
        }
        //去下单
        PayOrder payOrder = new PayOrder(
                channelId,
                orderCode,
                "购买"+bodyInfo+"道具下单",
                bodyInfo+"道具",
                IPUtil.getIPAddress(request),
                pageParam.getTUserID(),
                PayOrderTypeEnum.SX.getValue(),
                configPropOrder.getConfigPropOrderID(),
                pageParam.getPrice(),
                PayOrderBusinessTypeEnum.GMDJ.getValue(),
                new BigDecimal(0),
                LocalDateTime.now().plusMinutes(5L)
        );
        PayOrder pay = this.payOrderService.pay(payOrder, new User());
        String payReqRespInfo = pay.getPayReqResp();
        configPropOrder.setPayReqRespInfo(payReqRespInfo);
        if (pay.getExpireTime() != null) {
            long l = DateUtils.durationToMillis(LocalDateTime.now(), pay.getExpireTime());
            //加入延时队列
            TaskBase base = new TaskBase(configPropOrder.getServiceID(), 2, configPropOrder.getServiceID());
            //获取相差时间
            DelayTask task1 = new DelayTask(base, l);
            this.delayQueueManager.put(task1);
            log.info("订单：{}加入延时队列,过期时间{}", configPropOrder.getServiceID(), pay.getExpireTime());
        }
        return configPropOrder;
    }

}
